home *** CD-ROM | disk | FTP | other *** search
/ MaxiMac 2001 July / MaxiMac 116.iso / Macworld on CD n°116 / Mac OS X / Internet / Utilisation / Fizilla 5⁄30 / Components / nsHelperAppDlg.js < prev    next >
Encoding:
JavaScript  |  2001-05-29  |  23.6 KB  |  611 lines  |  [TEXT/CWIE]

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.1 (the "License"); you may not use this file except in
  5.  * compliance with the License.  You may obtain a copy of the License at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the License is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  10.  * for the specific language governing rights and limitations under the
  11.  * License.
  12.  *
  13.  * The Original Code is the Mozilla browser.
  14.  *
  15.  * The Initial Developer of the Original Code is Netscape Communications 
  16.  * Corporation.  Portions created by Netscape are 
  17.  * Copyright (C) 2001 Netscape Communications Corporation.  All Rights
  18.  * Reserved.
  19.  *
  20.  * Contributors:
  21.  *  Bill Law    <law@netscape.com>
  22.  */
  23.  
  24. /* This file implements the nsIHelperAppLauncherDialog interface.
  25.  *
  26.  * The implementation consists of a JavaScript "class" named nsHelperAppDialog,
  27.  * comprised of:
  28.  *   - a JS constructor function
  29.  *   - a prototype providing all the interface methods and implementation stuff
  30.  *
  31.  * In addition, this file implements an nsIModule object that registers the
  32.  * nsHelperAppDialog component.
  33.  */
  34.  
  35.  
  36. /* ctor
  37.  */
  38. function nsHelperAppDialog() {
  39.     // Initialize data properties.
  40.     this.mLauncher = null;
  41.     this.mContext  = null;
  42.     this.mSourcePath = null;
  43.     this.choseApp  = false;
  44.     this.chosenApp = null;
  45.     this.strings   = new Array;
  46.     this.elements  = new Array;
  47. }
  48.  
  49. nsHelperAppDialog.prototype = {
  50.     // Turn this on to get debugging messages.
  51.     debug: false,
  52.  
  53.     // Dump text (if debug is on).
  54.     dump: function( text ) {
  55.         if ( this.debug ) {
  56.             dump( text );
  57.         }
  58.     },
  59.  
  60.     // This "class" supports nsIHelperAppLauncherDialog, and nsISupports.
  61.     QueryInterface: function (iid) {
  62.         if (!iid.equals(Components.interfaces.nsIHelperAppLauncherDialog) &&
  63.             !iid.equals(Components.interfaces.nsISupports)) {
  64.             throw Components.results.NS_ERROR_NO_INTERFACE;
  65.         }
  66.         return this;
  67.     },
  68.  
  69.     // ---------- nsIHelperAppLauncherDialog methods ----------
  70.  
  71.     // show: Open XUL dialog using window watcher.  Since the dialog is not
  72.     //       modal, it needs to be a top level window and the way to open
  73.     //       one of those is via that route).
  74.     show: function(aLauncher, aContext)  {
  75.          this.mLauncher = aLauncher;
  76.          this.mContext  = aContext;
  77.          // Display the dialog using the Window Watcher interface.
  78.          var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  79.                     .getService( Components.interfaces.nsIWindowWatcher );
  80.          this.mDialog = ww.openWindow( null, // no parent
  81.                                        "chrome://global/content/nsHelperAppDlg.xul",
  82.                                        null,
  83.                                        "chrome,titlebar,dialog=yes",
  84.                                        null );
  85.          // Hook this object to the dialog.
  86.          this.mDialog.dialog = this;
  87.     },
  88.  
  89.     // promptForSaveToFile:  Display file picker dialog and return selected file.
  90.     promptForSaveToFile: function(aContext, aDefaultFile, aSuggestedFileExtension) {
  91.         var result = "";
  92.  
  93.         // Use file picker to show dialog.
  94.         var nsIFilePicker = Components.interfaces.nsIFilePicker;
  95.         var picker = Components.classes[ "@mozilla.org/filepicker;1" ]
  96.                        .createInstance( nsIFilePicker );
  97.         var bundle = Components.classes[ "@mozilla.org/intl/stringbundle;1" ]
  98.                        .getService( Components.interfaces.nsIStringBundleService )
  99.                            .createBundle( "chrome://global/locale/nsHelperAppDlg.properties");
  100.  
  101.         var windowTitle = bundle.GetStringFromName( "saveDialogTitle" );
  102.         
  103.         var parent = aContext
  104.                         .QueryInterface( Components.interfaces.nsIInterfaceRequestor )
  105.                         .getInterface( Components.interfaces.nsIDOMWindowInternal );
  106.         picker.init( parent, windowTitle, nsIFilePicker.modeSave );
  107.         picker.defaultString = aDefaultFile;
  108.  
  109.         var wildCardExtension = "*";
  110.         if ( aSuggestedFileExtension ) {
  111.             wildCardExtension += aSuggestedFileExtension;
  112.             picker.appendFilter( wildCardExtension, wildCardExtension );
  113.         }
  114.  
  115.         picker.appendFilters( nsIFilePicker.filterAll );
  116.  
  117.         // Pull in the user's preferences and get the default download directory.
  118.         var prefs = Components.classes[ "@mozilla.org/preferences;1" ]
  119.                         .getService( Components.interfaces.nsIPref );
  120.         try {
  121.             var startDir = prefs.getFileXPref( "browser.download.dir" );
  122.             if ( startDir.exists() ) {
  123.                 picker.displayDirectory = startDir;
  124.             }
  125.         } catch( exception ) {
  126.         }
  127.  
  128.         var dlgResult = picker.show();
  129.  
  130.         if ( dlgResult == nsIFilePicker.returnCancel ) {
  131.             throw Components.results.NS_ERROR_FAILURE;
  132.         }
  133.  
  134.  
  135.         // be sure to save the directory the user chose as the new browser.download.dir
  136.         result = picker.file;
  137.  
  138.         if ( result ) {
  139.             var newDir = result.parent;
  140.             prefs.setFileXPref( "browser.download.dir", newDir );
  141.         }
  142.         return result;
  143.     },
  144.     
  145.     // showProgressDialog:  For now, use old dialog.  At some point, the caller should be
  146.     //                      converted to use the new generic progress dialog (when it's
  147.     //                      finished).
  148.     showProgressDialog: function(aLauncher, aContext) {
  149.          // Display the dialog using the Window Watcher interface.
  150.          var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  151.                     .getService( Components.interfaces.nsIWindowWatcher );
  152.          ww.openWindow( null, // no parent
  153.                         "chrome://global/content/helperAppDldProgress.xul",
  154.                         null,
  155.                         "chrome,titlebar,minimizable,dialog=yes",
  156.                         aLauncher );
  157.     },
  158.     
  159.     // ---------- implementation methods ----------
  160.  
  161.     // initDialog:  Fill various dialog fields with initial content.
  162.     initDialog : function() {
  163.          // Check if file is executable (in which case, we will go straight to
  164.          // "save to disk").
  165.          var ignore1 = new Object;
  166.          var ignore2 = new Object;
  167.          var tmpFile = this.mLauncher.getDownloadInfo( ignore1, ignore2 );
  168.          if ( tmpFile.isExecutable() ) {
  169.              this.mLauncher.saveToDisk( null, false );
  170.              this.mDialog.close();
  171.              return;
  172.          }
  173.  
  174.          // Put product brand short name in prompt.
  175.          var prompt = this.dialogElement( "prompt" );
  176.          var modified = this.replaceInsert( prompt.firstChild.nodeValue, 1, this.getString( "brandShortName" ) );
  177.          prompt.firstChild.nodeValue = modified;
  178.  
  179.          // Put file name in window title.
  180.          var win   = this.dialogElement( "nsHelperAppDlg" );
  181.          var url   = this.mLauncher.source.QueryInterface( Components.interfaces.nsIURL );
  182.          var fname = "";
  183.          this.mSourcePath = url.prePath;
  184.          if ( url ) {
  185.              // A url, use file name from it.
  186.              fname = url.fileName;
  187.              this.mSourcePath += url.directory;
  188.          } else {
  189.              // A generic uri, use path.
  190.              fname = this.mLauncher.source.path;
  191.              this.mSourcePath += url.path;
  192.          }
  193.          var title = this.replaceInsert( win.getAttribute( "title" ), 1, fname );
  194.          win.setAttribute( "title", title );
  195.  
  196.          // Put content type and location into intro.
  197.          this.initIntro();
  198.  
  199.          // Add special debug hook.
  200.          if ( this.debug ) {
  201.              var prompt = this.dialogElement( "prompt" );
  202.              prompt.setAttribute( "onclick", "dialog.doDebug()" );
  203.          }
  204.  
  205.          // Put explanation of default action into text box.
  206.          this.initExplanation();
  207.  
  208.          // Set default selection (always the "default").
  209.          this.dialogElement( "default" ).checked = true;
  210.  
  211.          // If default is not to save to disk, then make that the alternative.
  212.          if ( this.mLauncher.MIMEInfo.preferredAction != Components.interfaces.nsIMIMEInfo.saveToDisk ) {
  213.              this.dialogElement( "saveToDisk" ).checked = true;
  214.          } else {
  215.              this.dialogElement( "openUsing" ).checked = true;
  216.          }
  217.  
  218.          // Disable selection under "different action".
  219.          this.option();
  220.  
  221.          // Set up dialog button callbacks.
  222.          var object = this; // "this.onOK()" doesn't work!
  223.          this.mDialog.doSetOKCancel( function () { return object.onOK(); },
  224.                                      function () { return object.onCancel(); } );
  225.  
  226.          // Position it.
  227.          if ( this.mDialog.opener ) {
  228.              this.mDialog.moveToAlertPosition();
  229.          } else {
  230.              this.mDialog.centerWindowOnScreen();
  231.          }
  232.     },
  233.  
  234.     // initIntro:
  235.     initIntro: function() {
  236.         var intro = this.dialogElement( "intro" );
  237.         var desc = this.mLauncher.MIMEInfo.Description;
  238.         if ( desc != "" ) {
  239.             // Use intro with descriptive text.
  240.             modified = this.replaceInsert( this.getString( "intro.withDesc" ), 1, this.mLauncher.MIMEInfo.Description );
  241.         } else {
  242.             // Use intro without descriptive text.
  243.             modified = this.getString( "intro.noDesc" );
  244.         }
  245.         modified = this.replaceInsert( modified, 2, this.mLauncher.MIMEInfo.MIMEType );
  246.         modified = this.replaceInsert( modified, 3, this.mSourcePath );
  247.         intro.firstChild.nodeValue = "";
  248.         intro.firstChild.nodeValue = modified;
  249.     },
  250.  
  251.     // initExplanation:
  252.     initExplanation: function() {
  253.         var expl = this.dialogElement( "explanation" );
  254.         if ( this.mLauncher.MIMEInfo.preferredAction == Components.interfaces.nsIMIMEInfo.saveToDisk ) {
  255.             expl.value = this.getString( "explanation.saveToDisk" );
  256.         } else {
  257.             // Default is to "open with system default."
  258.             var appDesc = this.getString( "explanation.defaultApp" );
  259.             if ( this.mLauncher.MIMEInfo.preferredAction != Components.interfaces.nsIMIMEInfo.useSystemDefault ) {
  260.                 // If opening using the app, we prefer to use the app description.
  261.                 appDesc = this.mLauncher.MIMEInfo.applicationDescription;
  262.                 if ( appDesc != "" ) {
  263.                     // Use application description.
  264.                     expl.value= this.replaceInsert( this.getString( "explanation.openUsing" ), 1, appDesc );
  265.                 } else {
  266.                     // If no description, use the app executable name.
  267.                     var app = this.mLauncher.MIMEInfo.preferredApplicationHandler;
  268.                     if ( app ) {
  269.                         // Use application path.
  270.                         expl.value = this.replaceInsert( this.getString( "explanation.openUsing" ), 1, app.unicodePath );
  271.                     }
  272.                 }
  273.             }
  274.         }
  275.     },
  276.  
  277.     // onOK:
  278.     onOK: function() {
  279.         // Do what the user asked...
  280.         if ( this.dialogElement( "default" ).checked ) {
  281.             var nsIMIMEInfo = Components.interfaces.nsIMIMEInfo;
  282.             // Get action from MIMEInfo...
  283.             if ( this.mLauncher.MIMEInfo.preferredAction == nsIMIMEInfo.saveToDisk ) {
  284.                 this.mLauncher.saveToDisk( null, false );
  285.             } else {
  286.                 this.mLauncher.launchWithApplication( this.mLauncher.MIMEInfo.preferredApplicationHandler, false );
  287.             }
  288.         } else {
  289.             // Something different for this file...
  290.             if ( this.dialogElement( "openUsing" ).checked ) {
  291.                 // If no app "chosen" then convert input string to file.
  292.                 if ( !this.chosenApp ) {
  293.                     var app = Components.classes[ "@mozilla.org/file/local;1" ].createInstance( Components.interfaces.nsILocalFile );
  294.                     app.initWithUnicodePath( this.dialogElement( "appName" ).value );
  295.                     if ( !app.exists() ) {
  296.                         // Show alert and try again.
  297.                         var msg = this.replaceInsert( this.getString( "badApp" ), 1, app.unicodePath );
  298.                         var svc = Components.classes[ "@mozilla.org/embedcomp/prompt-service;1" ]
  299.                                       .getService( Components.interfaces.nsIPromptService );
  300.                         svc.alert( this.mDialog, this.getString( "badApp.title" ), msg );
  301.                         // Disable the OK button.
  302.                         this.dialogElement( "ok" ).disabled = true;
  303.                         // Leave dialog up.
  304.                         return false;
  305.                     } else {
  306.                         // Use that app.
  307.                         this.chosenApp = app;
  308.                     }
  309.                 }
  310.                 this.mLauncher.launchWithApplication( this.chosenApp, false );
  311.             } else {
  312.                 this.mLauncher.saveToDisk( null, false );
  313.             }
  314.         }
  315.  
  316.         // Unhook dialog from this object.
  317.         this.mDialog.dialog = null;
  318.  
  319.         // Close up dialog by returning true.
  320.         return true;
  321.         //this.mDialog.close();
  322.     },
  323.  
  324.     // onCancel:
  325.     onCancel: function() {
  326.         // Cancel app launcher.
  327.         try {
  328.             this.mLauncher.Cancel();
  329.         } catch( exception ) {
  330.         }
  331.         
  332.         // Unhook dialog from this object.
  333.         this.mDialog.dialog = null;
  334.  
  335.         // Close up dialog by returning true.
  336.         return true;
  337.     },
  338.  
  339.     // option:
  340.     option: function() {
  341.         // If "different" option is checked, then enable selections under it.
  342.         var state = this.dialogElement( "different" ).checked;
  343.         this.dialogElement( "saveToDisk" ).disabled = !state;
  344.         this.dialogElement( "openUsing" ).disabled = !state;
  345.         // Propagate state change to subfields.
  346.         this.differentOption();
  347.     },
  348.  
  349.     // focusAppName:
  350.     focusAppName: function() {
  351.         var appName = this.dialogElement( "appName" );
  352.         appName.focus();
  353.         appName.select();
  354.     },
  355.  
  356.     // differentOption:
  357.     differentOption: function() {
  358.         // If openUsing checkbox is disabled or not selected, then disable subfields.
  359.         var openUsing = this.dialogElement( "openUsing" );
  360.         var state = !openUsing.disabled && openUsing.checked;
  361.         this.dialogElement( "appName" ).disabled = !state;
  362.         this.dialogElement( "chooseApp" ).disabled = !state;
  363.  
  364.         // If "openUsing" is enabled and checked, then focus there.
  365.         if ( state ) {
  366.             this.mDialog.setTimeout( "dialog.focusAppName()", 0 );
  367.         }
  368.  
  369.         // Update Ok button.
  370.         this.updateOKButton();
  371.     },
  372.  
  373.     // dialogElement:  Try cache; obtain from document if not there.
  374.     dialogElement: function( id ) {
  375.          // Check if we've already fetched it.
  376.          if ( !( id in this.elements ) ) {
  377.              // No, then get it from dialog.
  378.              this.elements[ id ] = this.mDialog.document.getElementById( id );
  379.          }
  380.          return this.elements[ id ];
  381.     },
  382.  
  383.     // updateOKButton: Disable/enable Ok button depending on whether we've got all we need.
  384.     updateOKButton: function() {
  385.         var ok = false;
  386.         if ( this.dialogElement( "default" ).checked ) {
  387.             // This is always OK.
  388.             ok = true;
  389.         } else {
  390.             if ( this.dialogElement( "saveToDisk" ).checked ) {
  391.                 // Save to disk is always Ok.
  392.                 ok = true;
  393.             } else {
  394.                 if ( this.chosenApp || this.dialogElement( "appName" ).value != "" ) {
  395.                     // Open using is OK if app selected.
  396.                     ok = true;
  397.                 }
  398.             }
  399.         }
  400.         // Enable Ok button if ok to press.
  401.         this.dialogElement( "ok" ).disabled = !ok;
  402.     },
  403.  
  404.     // chooseApp:  Open file picker and prompt user for application.
  405.     chooseApp: function() {
  406.         var nsIFilePicker = Components.interfaces.nsIFilePicker;
  407.         var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance( nsIFilePicker );
  408.         fp.init( this.mDialog,
  409.                  this.getString( "chooseAppFilePickerTitle" ),
  410.                  nsIFilePicker.modeOpen );
  411.  
  412.         // XXX - We want to say nsIFilePicker.filterExecutable or something
  413.         fp.appendFilters( nsIFilePicker.filterAll );
  414.         
  415.         if ( fp.show() == nsIFilePicker.returnOK && fp.file ) {
  416.             // Remember the file they chose to run.
  417.             this.userChoseApp = true;
  418.             this.chosenApp    = fp.file;
  419.             // Update dialog.
  420.             this.dialogElement( "appName" ).value = this.chosenApp.unicodePath;
  421.         }
  422.     },
  423.  
  424.     // setDefault:  Open "edit MIMEInfo" dialog (borrowed from prefs).
  425.     setDefault: function() {
  426.         // Get RDF service.
  427.         var rdf = Components.classes[ "@mozilla.org/rdf/rdf-service;1" ]
  428.                     .getService( Components.interfaces.nsIRDFService );
  429.         // Now ask if it knows about this mime type.
  430.         var exists = false;
  431.         var fileLocator = Components.classes[ "@mozilla.org/file/directory_service;1" ]
  432.                             .getService( Components.interfaces.nsIProperties );
  433.         var file        = fileLocator.get( "UMimTyp", Components.interfaces.nsIFile );
  434.         var file_url    = Components.classes[ "@mozilla.org/network/standard-url;1" ]
  435.                             .createInstance( Components.interfaces.nsIFileURL );
  436.         file_url.file = file;
  437.  
  438.         // We must try creating a fresh remote DS in order to avoid accidentally
  439.         // having GetDataSource trigger an asych load.
  440.         var ds = Components.classes[ "@mozilla.org/rdf/datasource;1?name=xml-datasource" ].createInstance( Components.interfaces.nsIRDFDataSource );
  441.         try {
  442.             // Initialize it.  This will fail if the uriloader (or anybody else)
  443.             // has already loaded/registered this data source.
  444.             var remoteDS = ds.QueryInterface( Components.interfaces.nsIRDFRemoteDataSource );
  445.             remoteDS.Init( file_url.spec );
  446.             remoteDS.Refresh( true );
  447.         } catch ( all ) {
  448.             // OK then, presume it was already registered; get it.
  449.             ds = rdf.GetDataSource( file_url.spec );
  450.         }
  451.  
  452.         // Now check if this mimetype is really in there;
  453.         // This is done by seeing if there's a "value" arc from the mimetype resource
  454.         // to the mimetype literal string.
  455.         var mimeRes       = rdf.GetResource( "urn:mimetype:" + this.mLauncher.MIMEInfo.MIMEType );
  456.         var valueProperty = rdf.GetResource( "http://home.netscape.com/NC-rdf#value" );
  457.         var mimeLiteral   = rdf.GetLiteral( this.mLauncher.MIMEInfo.MIMEType );
  458.         exists =  ds.HasAssertion( mimeRes, valueProperty, mimeLiteral, true );
  459.  
  460.         var dlgUrl;
  461.         if ( exists ) {
  462.             // Open "edit mime type" dialog.
  463.             dlgUrl = "chrome://communicator/content/pref/pref-applications-edit.xul";
  464.         } else {
  465.             // Open "add mime type" dialog.
  466.             dlgUrl = "chrome://communicator/content/pref/pref-applications-new.xul";
  467.         }
  468.  
  469.         // Open whichever dialog is appropriate, passing this dialog object as argument.
  470.         this.mDialog.openDialog( dlgUrl,
  471.                                  "_blank",
  472.                                  "chrome,modal=yes,resizable=no",
  473.                                  this );
  474.  
  475.         // Refresh dialog with updated info about the default action.
  476.         this.initIntro();
  477.         this.initExplanation();
  478.     },
  479.  
  480.     // updateMIMEInfo:  This is called from the pref-applications-edit dialog when the user
  481.     //                  presses OK.  Take the updated MIMEInfo and have the helper app service
  482.     //                  "write" it back out to the RDF datasource.
  483.     updateMIMEInfo: function() {
  484.         this.dump( "updateMIMEInfo called...\n" );
  485.         this.dumpObjectProperties( "\tMIMEInfo", this.mLauncher.MIMEInfo );
  486.     },
  487.  
  488.     // dumpInfo:
  489.     doDebug: function() {
  490.         const nsIProgressDialog = Components.interfaces.nsIProgressDialog;
  491.         // Open new progress dialog.
  492.         var progress = Components.classes[ "@mozilla.org/progressdialog;1" ]
  493.                          .createInstance( nsIProgressDialog );
  494.         // Show it.
  495.         progress.open( this.mDialog );
  496.     },
  497.  
  498.     // dumpObj:
  499.     dumpObj: function( spec ) {
  500.          var val = "<undefined>";
  501.          try {
  502.              val = eval( "this."+spec ).toString();
  503.          } catch( exception ) {
  504.          }
  505.          this.dump( spec + "=" + val + "\n" );
  506.     },
  507.  
  508.     // dumpObjectProperties
  509.     dumpObjectProperties: function( desc, obj ) {
  510.          for( prop in obj ) {
  511.              this.dump( desc + "." + prop + "=" );
  512.              var val = "<undefined>";
  513.              try {
  514.                  val = obj[ prop ];
  515.              } catch ( exception ) {
  516.              }
  517.              this.dump( val + "\n" );
  518.          }
  519.     },
  520.  
  521.     // getString: Fetch data string from dialog content (and cache it).
  522.     getString: function( id ) {
  523.         // Check if we've fetched this string already.
  524.         if ( !( id in this.strings ) ) {
  525.             // Try to get it.
  526.             var elem = this.mDialog.document.getElementById( id );
  527.             if ( elem
  528.                  &&
  529.                  elem.firstChild
  530.                  &&
  531.                  elem.firstChild.nodeValue ) {
  532.                 this.strings[ id ] = elem.firstChild.nodeValue;
  533.             } else {
  534.                 // If unable to fetch string, use an empty string.
  535.                 this.strings[ id ] = "";
  536.             }
  537.         }
  538.         return this.strings[ id ];
  539.     },
  540.  
  541.     // replaceInsert: Replace given insert with replacement text and return the result.
  542.     replaceInsert: function( text, insertNo, replacementText ) {
  543.         var result = text;
  544.         var regExp = eval( "/#"+insertNo+"/" );
  545.         result = result.replace( regExp, replacementText );
  546.         return result;
  547.     }
  548. }
  549.  
  550. // This Component's module implementation.  All the code below is used to get this
  551. // component registered and accessible via XPCOM.
  552. var module = {
  553.     firstTime: true,
  554.  
  555.     // registerSelf: Register this component.
  556.     registerSelf: function (compMgr, fileSpec, location, type) {
  557.         if (this.firstTime) {
  558.             this.firstTime = false;
  559.             throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
  560.         }
  561.         compMgr.registerComponentWithType( this.cid,
  562.                                            "Mozilla Helper App Launcher Dialog",
  563.                                            this.contractId,
  564.                                            fileSpec,
  565.                                            location,
  566.                                            true,
  567.                                            true,
  568.                                            type );
  569.     },
  570.  
  571.     // getClassObject: Return this component's factory object.
  572.     getClassObject: function (compMgr, cid, iid) {
  573.         if (!cid.equals(this.cid)) {
  574.             throw Components.results.NS_ERROR_NO_INTERFACE;
  575.         }
  576.  
  577.         if (!iid.equals(Components.interfaces.nsIFactory)) {
  578.             throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  579.         }
  580.  
  581.         return this.factory;
  582.     },
  583.  
  584.     /* CID for this class */
  585.     cid: Components.ID("{F68578EB-6EC2-4169-AE19-8C6243F0ABE1}"),
  586.  
  587.     /* Contract ID for this class */
  588.     contractId: "@mozilla.org/helperapplauncherdialog;1",
  589.  
  590.     /* factory object */
  591.     factory: {
  592.         // createInstance: Return a new nsProgressDialog object.
  593.         createInstance: function (outer, iid) {
  594.             if (outer != null)
  595.                 throw Components.results.NS_ERROR_NO_AGGREGATION;
  596.  
  597.             return (new nsHelperAppDialog()).QueryInterface(iid);
  598.         }
  599.     },
  600.  
  601.     // canUnload: n/a (returns true)
  602.     canUnload: function(compMgr) {
  603.         return true;
  604.     }
  605. };
  606.  
  607. // NSGetModule: Return the nsIModule object.
  608. function NSGetModule(compMgr, fileSpec) {
  609.     return module;
  610. }
  611.